#pragma once

/*--------------------------------------------------------------------------*/

struct EVertexDeclaration
{
	enum Type
	{
	    Position0		= 0,
	    Position1,
	    Normal0,
	    Normal1,
	    Texcoord0,
	    Texcoord1,
	    Texcoord2,
	    Texcoord3,
	    Color0,
	    Color1,

	    /*---Instancing-------------------------------------------------------------*/
	    Instancing,

	    /*---Fonts------------------------------------------------------------------*/
	    FontData,

	    /*---Meshes-----------------------------------------------------------------*/
	    BasicVertex,

	    /*---Voxels-----------------------------------------------------------------*/
	    VoxelType,

	    Indices,
	    Unknown
	};

	static const char* ToStr(Type t)
	{
		switch (t)
		{
#define MAKE_CASE(a) case a: return #a;
			MAKE_CASE(Position0)
			MAKE_CASE(Position1)
			MAKE_CASE(Normal0)
			MAKE_CASE(Normal1)
			MAKE_CASE(Texcoord0)
			MAKE_CASE(Texcoord1)
			MAKE_CASE(Texcoord2)
			MAKE_CASE(Texcoord3)
			MAKE_CASE(Color0)
			MAKE_CASE(Color1)
			MAKE_CASE(Instancing)
			MAKE_CASE(FontData)
			MAKE_CASE(BasicVertex)
			MAKE_CASE(VoxelType)
			MAKE_CASE(Indices)
			MAKE_CASE(Unknown)
#undef MAKE_CASE

		default:
			AssertLogC(true, "Unknown EVertexDeclaration");
		}

		return "UNKNOWN!";
	}

	static Type FromStr(const char* s)
	{
#define MAKE_IF(a)		if(strcmp(s, #a) == 0)		{ return a; }
#define ELSE_MAKE_IF(a) else if(strcmp(s, #a) == 0) { return a; }

		MAKE_IF(Position0)
		ELSE_MAKE_IF(Position1)
		ELSE_MAKE_IF(Normal0)
		ELSE_MAKE_IF(Normal1)
		ELSE_MAKE_IF(Texcoord0)
		ELSE_MAKE_IF(Texcoord1)
		ELSE_MAKE_IF(Texcoord2)
		ELSE_MAKE_IF(Texcoord3)
		ELSE_MAKE_IF(Color0)
		ELSE_MAKE_IF(Color1)
		ELSE_MAKE_IF(Instancing)
		ELSE_MAKE_IF(VoxelType)
		ELSE_MAKE_IF(FontData)
		ELSE_MAKE_IF(BasicVertex)
		ELSE_MAKE_IF(Indices)
		return Unknown;

#undef ELSE_MAKE_IF
#undef MAKE_IF
	}
};

struct VertexDeclaration
{
	EVertexDeclaration::Type	m_semantic;
	uint						m_location;
	uint						m_cols;
	uint						m_rows;
	GLenum						m_type;
	uint						m_size;
	bool						m_instanced;
	std::string					m_glslType;
};

extern const VertexDeclaration g_vertexDeclarations[];

/*--------------------------------------------------------------------------*/

struct FontData
{
	glm::vec2 m_texcoord;
	glm::vec2 m_size;
	glm::vec2 m_position;
};

/*--------------------------------------------------------------------------*/

struct GLVertexBuffer
{
	const std::string	name;
	const GLenum		type;
	uint				handle;
	EVertexDeclaration::Type semantic;
	uint				size;
	uchar*				shadow;
	GLenum				access;
	uint				updateBeginByte;
	uint				updateEndByte;
	bool				mapped;
	GLVertexArray*		owner;

	GLVertexBuffer(const std::string& name, GLenum type, uint sizeBytes, GLenum access) :
		name(name),
		type(type),
		handle(0),
		size(sizeBytes),
		shadow(NULL),
		access(access),
		mapped(false),
		owner(NULL)
	{
		shadow = new uchar[sizeBytes];
		memset(shadow, 0, sizeBytes);

		updateEndByte = 0;
		updateBeginByte = sizeBytes;
	}

	~GLVertexBuffer()
	{
		if (shadow)
		{
			delete [] shadow;
		}
	}
};

/*--------------------------------------------------------------------------*/

class VertexBufferWrapper
{
protected:
	VertexBufferWrapper();
	~VertexBufferWrapper();

	static GLVertexBuffer*	_boundElementArrayBuffer;
	static GLVertexBuffer*	_boundArrayBuffer;
	static int				_vertexBuffersCount;
	static std::map<std::string, GLVertexBuffer*>	_vertexBuffersMap;

public:
	static GLVertexBuffer*	GenBuffer(const std::string& name, GLenum type, uint sizeBytes, GLenum access = GL_STATIC_DRAW);
	static GLVertexBuffer*	GenBuffer(const std::string& name, EVertexDeclaration::Type semantic, uint numVertices, GLenum access = GL_STATIC_DRAW);
	static void				DeleteBuffer(GLVertexBuffer** vb);

	static void*			MapBuffer(GLVertexBuffer* vb);
	static void				UnmapBuffer(GLVertexBuffer* vb);

	static void				BindBuffer(GLVertexBuffer* vb, bool force = false);
	static void				UnbindBuffers();

	static void*			GetShadowBuffer(GLVertexBuffer* vb);
	static void*			GetShadowBuffer(GLVertexBuffer* vb, uint sdata, uint offset);
	static void				CopyDataToShadow(GLVertexBuffer* vb, void* data, uint sdata, uint offset);
	static void				FlushShadowBuffer(GLVertexBuffer* vb);

	static int				GetSemanticLocation(const std::string& semantic);
	static std::string		GetSemanticType(const std::string& semantic);

	static GLVertexBuffer*	GetBuffer(const std::string& name);
};

